//
// average_background.cpp
//
// author: vietcuong
//

#include "average_background.h"

#define HIGH_SCALE_NUMBER 5.0
#define LOW_SCALE_NUMBER 5.0

AverageBackground::AverageBackground() {
    frame_count = 0.00001;
}

void AverageBackground::init(IplImage *image) {
    IavgF = cvCreateImage(cvGetSize(image), IPL_DEPTH_32F, 3);
    IdiffF = cvCreateImage(cvGetSize(image), IPL_DEPTH_32F, 3);
    IprevF = cvCreateImage(cvGetSize(image), IPL_DEPTH_32F, 3);

    IhighF = cvCreateImage(cvGetSize(image), IPL_DEPTH_32F, 3);
    IlowF = cvCreateImage(cvGetSize(image), IPL_DEPTH_32F, 3);

    cvZero(IavgF);
    cvZero(IdiffF);
    cvZero(IprevF);
    cvZero(IhighF);
    cvZero(IlowF);


    Ihigh1 = cvCreateImage(cvGetSize(image), IPL_DEPTH_32F, 1);
    Ihigh2 = cvCreateImage(cvGetSize(image), IPL_DEPTH_32F, 1);
    Ihigh3 = cvCreateImage(cvGetSize(image), IPL_DEPTH_32F, 1);
    Ilow1 = cvCreateImage(cvGetSize(image), IPL_DEPTH_32F, 1);
    Ilow2 = cvCreateImage(cvGetSize(image), IPL_DEPTH_32F, 1);
    Ilow3 = cvCreateImage(cvGetSize(image), IPL_DEPTH_32F, 1);

    Iscratch = cvCreateImage(cvGetSize(image), IPL_DEPTH_32F, 3);
    Iscratch2 = cvCreateImage(cvGetSize(image), IPL_DEPTH_32F, 3);
    cvZero(Iscratch);
    cvZero(Iscratch2);

    Igray1 = cvCreateImage(cvGetSize(image), IPL_DEPTH_32F, 1);
    Igray2 = cvCreateImage(cvGetSize(image), IPL_DEPTH_32F, 1);
    Igray3 = cvCreateImage(cvGetSize(image), IPL_DEPTH_32F, 1);
    Imask_tmp = cvCreateImage(cvGetSize(image), IPL_DEPTH_8U, 1);
}

void AverageBackground::deallocate_images() {
    cvReleaseImage(&IavgF);
    cvReleaseImage(&IdiffF);
    cvReleaseImage(&IprevF);

    cvReleaseImage(&IhighF);
    cvReleaseImage(&IlowF);

    cvReleaseImage(&Ihigh1);
    cvReleaseImage(&Ihigh2);
    cvReleaseImage(&Ihigh3);
    cvReleaseImage(&Ilow1);
    cvReleaseImage(&Ilow2);
    cvReleaseImage(&Ilow3);

    cvReleaseImage(&Iscratch);
    cvReleaseImage(&Iscratch2);

    cvReleaseImage(&Igray1);
    cvReleaseImage(&Igray2);
    cvReleaseImage(&Igray3);
    cvReleaseImage(&Imask_tmp);
}

void AverageBackground::setScaleHigh(float scale) {
    cvConvertScale(IdiffF, Iscratch, scale);
    cvAdd(IavgF, Iscratch, IhighF);
    cvCvtPixToPlane(IhighF, Ihigh1, Ihigh2, Ihigh3, 0);
}

void AverageBackground::setScaleLow(float scale) {
    cvConvertScale(IdiffF, Iscratch, scale);
    cvSub(IavgF, Iscratch, IlowF);
    cvCvtPixToPlane(IlowF, Ilow1, Ilow2, Ilow3, 0);
}

void AverageBackground::accumulate_background(IplImage *image) {
    static int first = 1;

    // Convert to float
    cvCvtScale(image, Iscratch, 1, 0);

    if (first == 1) {
        cvAcc(Iscratch, IavgF);
        cvAbsDiff(Iscratch, IprevF, Iscratch2);
        cvAcc(Iscratch2, IdiffF);
        frame_count += 1.0;
    }

    first = 0;
    cvCopy(Iscratch, IprevF);
}

void AverageBackground::create_model() {
    cvConvertScale(IavgF, IavgF, (double)(1.0/frame_count));
    cvConvertScale(IdiffF, IdiffF, (double)(1.0/frame_count));

    cvAddS(IdiffF, cvScalar(1.0, 1.0, 1.0), IdiffF);

    setScaleHigh(HIGH_SCALE_NUMBER);
    setScaleLow(LOW_SCALE_NUMBER);
}

void AverageBackground::find_foreground(IplImage *image, IplImage *Imask) {
    cvCvtScale(image, Iscratch, 1, 0);
    cvCvtPixToPlane(Iscratch, Igray1, Igray2, Igray3, 0);

    cvInRange(Igray1, Ilow1, Ihigh1, Imask);
    cvInRange(Igray2, Ilow2, Ihigh2, Imask_tmp);
    cvOr(Imask_tmp, Imask, Imask);

    cvInRange(Igray3, Ilow3, Ihigh3, Imask_tmp);
    cvOr(Imask_tmp, Imask, Imask);

    cvSubRS(Imask, cvScalar(255), Imask);
}
